package com.easylinkin.linkappapi.camera.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.easylinkin.linkappapi.camera.entity.CameraEntity;
import com.easylinkin.linkappapi.camera.entity.PhotoInfo;
import com.easylinkin.linkappapi.camera.service.CameraService;
import com.easylinkin.linkappapi.common.model.RequestModel;
import com.easylinkin.linkappapi.common.service.OSSService;
import com.easylinkin.linkappapi.common.utils.DateUtil;
import com.easylinkin.linkappapi.common.utils.file.FileExt;
import com.easylinkin.linkappapi.common.utils.file.ZipFileUtil;
import com.easylinkin.linkappapi.device.entity.Device;
import com.easylinkin.linkappapi.device.service.DeviceService;
import com.easylinkin.linkappapi.deviceattributestatus.service.impl.DeviceAttributeStatusServiceImpl;
import com.easylinkin.linkappapi.elasticsearch.entity.ESconfig;
import com.easylinkin.linkappapi.elasticsearch.service.IEService;
import com.easylinkin.linkappapi.elasticsearch.util.EsCalendar;
import com.easylinkin.linkappapi.facerecognition.service.oss.ObjectStorageInternalService;
import com.easylinkin.linkappapi.linkage.entity.LinkageConfigRefDownDevice;
import com.easylinkin.linkappapi.photoalbum.util.ExportUtil;
import com.easylinkin.linkappapi.ruleengine.entity.RuleCondition;
import com.easylinkin.linkappapi.ruleengine.entity.RuleEngine;
import com.easylinkin.linkappapi.ruleengine.entity.RuleExecution;
import com.easylinkin.linkappapi.ruleengine.entity.RuleTrigger;
import com.easylinkin.linkappapi.ruleengine.mapper.RuleEngineMapper;
import com.easylinkin.linkappapi.ruleengine.service.RuleConditionService;
import com.easylinkin.linkappapi.ruleengine.service.RuleExecutionService;
import com.easylinkin.linkappapi.ruleengine.service.RuleTriggerService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.mapper.LinkappTenantMapper;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

@Service
public class CameraServiceImpl  implements CameraService {

  private static final Logger LOGGER = LoggerFactory.getLogger(CameraServiceImpl.class);

  @Autowired
  private ESconfig eSconfig;

  private final String prefix = "形象抓拍";

  @Autowired
  @Qualifier("ossInternal")
  private ObjectStorageInternalService ossClient;

  @Autowired
  private OSSService ossService;

  @Value("${oss.bucket}")
  private String bucket;

  @Resource
  LinkappUserContextProducer linkappUserContextProducer;

  @Resource
  private RuleEngineMapper ruleEngineMapper;

  @Resource
  private RuleExecutionService ruleExecutionService;

  @Resource
  private RuleTriggerService ruleTriggerService;

  @Resource
  private DeviceService deviceService;

  @Resource
  private LinkappTenantService linkappTenantService;


  @Override
  public IPage<Map> getHistoricDataFromEs(RequestModel<CameraEntity> requestModel) {
    Page page = requestModel.getPage();
    SearchHits searchHits = getIPageFromEs(requestModel);
    if (searchHits == null) {
      for (int i = 1; i <= 3; i++) {
        LOGGER.info("定位历史数据查询为空问题，查询searchHits是null,重试第：" + i + " 次");
        try {
          Thread.sleep(200 * i);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
        searchHits = getIPageFromEs(requestModel);
        if (searchHits != null) {
          break;
        }
      }
      if (searchHits == null) {
        LOGGER.info("定位历史数据查询为空问题，查询searchHits是null,直接返回空结果！");
        return page;
      }
    }
    LOGGER.info("es查询结果到设备历史数据条数 ：" + searchHits.getTotalHits());
    SearchHit[] results = searchHits.getHits();

    List<Map<String, Object>> records = parseResultFromSearchHit(results);
    if(page==null){
      page = new Page();
      page.setTotal(searchHits.getTotalHits());
    }

    page.setRecords(records);
    page.setTotal(searchHits.getTotalHits());

    return page;
  }

  public List<Map<String, Object>> parseResultFromSearchHit(SearchHit[] results) {
    List<Map<String, Object>> records = new ArrayList<>();
    for (SearchHit hit : results) {
      Object data = hit.getSourceAsMap().get("data");
      if (ObjectUtils.isEmpty(data)) {
        continue;
      }
      Map<String, Object> sourceMap1 = (Map<String, Object>) data;
      for (Entry<String, Object> item : sourceMap1.entrySet()) {
        try {
//                       如果是时间戳的long 类型 返回， 时间格式 1.590406860678E12 原时间
          if (item.getKey().toLowerCase().contains("time")) {
            Double doubleTimeValue = Double.parseDouble(String.valueOf(item.getValue()));
            if (String.valueOf(doubleTimeValue.longValue()).length() == 13) {
              sourceMap1.put(item.getKey(), DateUtil.getYYYYMMDDHHMMSSDate(new Date(doubleTimeValue.longValue())));
            }
          }
        } catch (Exception e) {
          LOGGER.info("时间转换有异常：{}", e.getMessage());
        }
      }

      Map<String, Object> sourceMap = hit.getSourceAsMap();
      List<String> basicInfos = Arrays
          .asList("deviceCode", "deviceName", "deviceTypeName", "deviceUnitCode", "createTime","projectId","projectName");
      sourceMap.forEach((key, value) -> {
        if (basicInfos.indexOf(key) != -1) {
          sourceMap1.put(key, value);
        }
      });
      records.add(sourceMap1);
    }
    return records;
  }

  private SearchHits getIPageFromEs(RequestModel<CameraEntity> requestModel) {
    Page page = requestModel.getPage();
    String deviceCode = requestModel.getCustomQueryParams().getDeviceCode();
    Long projectId = requestModel.getCustomQueryParams().getProjectId();
    String projectIds = requestModel.getCustomQueryParams().getProjectIds();
    Integer isImage = requestModel.getCustomQueryParams().getIsImage();
    String beginTime = requestModel.getCustomQueryParams().getQueryTimeStart();
    String endTime = requestModel.getCustomQueryParams().getQueryTimeEnd();
    CameraEntity cameraEntity = requestModel.getCustomQueryParams();
    Integer enterpriseSourceType = cameraEntity.getEnterpriseSourceType();
    // 查询条件，可以参考官网手册
    BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
    if(StringUtils.isNotBlank(deviceCode)){
      boolQuery.filter(QueryBuilders.termQuery("deviceCode", deviceCode));
    }
    if(StringUtils.isNotBlank(projectIds)){
      String[] split = projectIds.split(",");
      if(split.length>0){
        QueryBuilder termsQuery = QueryBuilders.termsQuery("projectId",Arrays.asList(split));
        boolQuery.filter(termsQuery);
      }
    }
    if(projectId!=null){
      boolQuery.must(QueryBuilders.matchPhraseQuery("projectId", projectId));
    }else{
      if(enterpriseSourceType != null && enterpriseSourceType.intValue() == 2){
        //企业级请求
        //查询现在还存在的所有项目
        List<LinkappTenant> tenantLs = linkappTenantService.selectAll();
        List<String> projectLs = null;
        if(CollectionUtil.isNotEmpty(tenantLs)){
          projectLs = tenantLs.stream().map(e -> e.getProjectId()).collect(Collectors.toList());
          QueryBuilder termsQuery = QueryBuilders.termsQuery("projectId",projectLs);
          boolQuery.filter(termsQuery);
          //张磊反馈如果用must，传集合参数，会一条数据都查询不到
          //MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("projectId", projectLs);
          //boolQuery.must(matchPhraseQueryBuilder);
        }
      }
    }
    boolQuery.must(QueryBuilders.matchPhraseQuery("data.isImage", isImage));
    boolQuery.filter(QueryBuilders.rangeQuery("createTime").gte(beginTime).lte(endTime).format("yyyy-MM-dd HH:mm:ss"));
    QueryBuilder queryBuilder = QueryBuilders.boolQuery();
    QueryBuilder existQuery = QueryBuilders.existsQuery("data.isImage");
    ((BoolQueryBuilder) queryBuilder).should(existQuery);
    // es 6.0以上支持RestHighLevelClient
    RestHighLevelClient hclient = eSconfig.getFactory().getRhlClient();

    String indices = EsCalendar.getDeviceFlowIndex(beginTime, endTime);
    SearchRequest searchRequest = new SearchRequest(indices);
    //忽略不可用索引，允许索引不不存在，通配符表达式将扩展为打开的索引
    searchRequest.indicesOptions(IndicesOptions.fromOptions(true, true, true, false));
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    Assert.notNull(deviceCode, "查询Es，设备编号不能为空!");
//        boolQuery.must(QueryBuilders.matchPhraseQuery("deviceCode", deviceCode));
//        boolQuery.must(queryBuilder);
    boolQuery.filter(queryBuilder);

    searchSourceBuilder.query(boolQuery);
    String[] includeFields = new String[]{};
    String[] excludeFields = new String[]{"sourceRef"};
    searchSourceBuilder.fetchSource(includeFields, excludeFields);
    if(page!=null){
      Assert.isTrue(page.getSize() <= 10000, "超过最大查询数，请缩小查询范围");
      Long index = page.getCurrent();
      index--;
      searchSourceBuilder.from((int)(index * page.getSize())).size((int)(page.getSize()));  //取数大小
    }
    //降序排列
    searchSourceBuilder.sort("createTime", SortOrder.DESC);
    searchRequest.source(searchSourceBuilder);
    try {
      long startTime = System.currentTimeMillis();
      // 查询结果
      LOGGER.info("设备数据详情ES请求数据：" + searchRequest);
      SearchResponse searchResponse = hclient.search(searchRequest, RequestOptions.DEFAULT);
      SearchHits hits = searchResponse.getHits();
      long sysEndTime = System.currentTimeMillis();
      LOGGER.info("查询用时:" + (sysEndTime - startTime));
      return hits;
    } catch (Exception e) {
      LOGGER.error("getDeviceDataListFromES 搜索ES数据,索引:" + indices + "异常:{}", e);
      e.printStackTrace();
    } finally {
    }
    return null;
  }

  @Override
  public String exportPhoto(CameraEntity dto) {
    List<PhotoInfo> urls = dto.getUrls();
    List<FileExt> fileExtList = new ArrayList<>();
    String fileName="";
    String dateStr = DateUtil.getyyyyMMddHHmmssSSSNumberString(new Date());

    for (PhotoInfo info : urls) {
      String url = info.getUrl();
      if (StringUtils.isNotEmpty(url)) {
        url = url.replace("+", "%2B");
        url = url.replace("&", "%26");
      } else {
        LOGGER.error("打包失败：名称为空");
        continue;
      }
      FileExt fileInfoByUrl = ExportUtil
          .getFileInfoByUrl(url,prefix,dateStr,"",Long.valueOf(DateUtil.getyyyyMMddHHmmNumberString(info.getCreateTime())));
      if (null != fileInfoByUrl && null != fileInfoByUrl.getInputStream()) {
        fileExtList.add(fileInfoByUrl);
      } else {
        LOGGER.error("打包失败：名称【{}】", url);
      }
    }
    //开始导出
    InputStream fileInputStream = null;
    try {
      fileInputStream = ZipFileUtil.getFileInputStream(dateStr, fileExtList);
      //上传文件到oss
      String resultFilename =prefix+"_"+dto.getDeviceName()+"_"+ dateStr + ".zip";
      ossClient.putObject(bucket, resultFilename, fileInputStream, "application/octet-stream");
      fileName = ossService.generateUrl(resultFilename);
    } catch (Exception e) {
      LOGGER.error("打包失败：", e);
    } finally {
      // 关闭流
      try {
        if (null != fileInputStream) {
          fileInputStream.close();
        }
      } catch (IOException e) {
        LOGGER.error("打包失败：", e);
      }
    }
    return fileName;
  }

  @Override
  public List<CameraEntity> getCameraList(CameraEntity dto) {
    //获取相关的规则名称
    QueryWrapper<RuleEngine> reqw = new QueryWrapper<>();
    String tenantId = null;
    String projectIds = dto.getProjectIds();
    if(dto.getProjectId()==null && StringUtils.isBlank(projectIds)){
      tenantId = linkappUserContextProducer.getNotNullCurrent().getTenantId();
      if(StringUtils.isBlank(tenantId)){
        return new ArrayList<>();
      }
      reqw.eq("tenant_id", tenantId);
    }else {
      if(StringUtils.isBlank(projectIds)){
        LinkappTenant one = linkappTenantService.getOneByProjectId(dto.getProjectId());
        if(one==null){
          return new ArrayList<>();
        }
        tenantId = one.getId();
        if(StringUtils.isBlank(tenantId)){
          return new ArrayList<>();
        }
        reqw.eq("tenant_id", tenantId);
      }else {
        String[] split = projectIds.split(",");
        if(CollectionUtil.isEmpty(Arrays.asList(split))){
          return new ArrayList<>();
        }
        reqw.in("tenant_id", split);
      }
    }

    final String tenantIdCopy = tenantId;
    reqw.likeRight("name", "形象抓拍-");
    List<RuleEngine> ruleEngineList = ruleEngineMapper.selectList(reqw);
    List<CameraEntity> cameraEntities = new ArrayList<>();
    List<String> existCodes = new ArrayList<>();
    Map<String,LinkappTenant> cache = new HashMap<>();
    if(CollectionUtil.isNotEmpty(ruleEngineList)){
      ruleEngineList.forEach(ruleEngine->{
        RuleTrigger ruleTrigger = new RuleTrigger();
        ruleTrigger.setRuleEngineId(ruleEngine.getId());
        List<RuleTrigger> ruleTriggersAndExpressions = ruleTriggerService
            .getRuleTriggersAndExpressions(ruleTrigger);
        //如果触发规则中有定时下发，则有效
        List<RuleTrigger> ruleConditions = ruleTriggersAndExpressions.stream()
            .filter(item -> item.getType() != null && item.getType().equals(3)).collect(
                Collectors.toList());
        if(CollectionUtil.isNotEmpty(ruleConditions)){
          RuleExecution ruleExecution = new RuleExecution();
          ruleExecution.setRuleEngineId(ruleEngine.getId());
          List<RuleExecution> ruleExecutionDetails = ruleExecutionService
              .getRuleExecutionDetails(ruleExecution);
          ruleExecutionDetails.forEach(item->{
            List<LinkageConfigRefDownDevice> linkageConfigRefDownDeviceList = item
                .getLinkageConfigRefDownDeviceList();
            if(CollectionUtil.isNotEmpty(linkageConfigRefDownDeviceList)){
              linkageConfigRefDownDeviceList.forEach(linkageConfigRefDownDevice->{
                String deviceCode = linkageConfigRefDownDevice.getDeviceCode();
                if(!existCodes.contains(deviceCode)){
                  Device device = new Device();
                  device.setTenantId(tenantIdCopy);
                  device.setCode(deviceCode);
                  device.setDeleteState(1);
                  List<Device> devices = deviceService.selectDevices(device);
                  if(CollectionUtil.isNotEmpty(devices)){
                    Device device1 = devices.get(0);
                    CameraEntity cameraEntity = new CameraEntity();
                    cameraEntity.setDeviceCode(device1.getCode());
                    cameraEntity.setDeviceName(device1.getName());
                    LinkappTenant linkappTenant = cache.get(device1.getTenantId());
                    if(linkappTenant ==null){
                      linkappTenant = linkappTenantService.getOneById(device1.getTenantId());
                      if(linkappTenant!=null){
                        cache.put(device1.getTenantId(),linkappTenant);
                        cameraEntity.setProjectId(Long.valueOf(linkappTenant.getProjectId()));
                      }
                    }else {
                      cameraEntity.setProjectId(Long.valueOf(linkappTenant.getProjectId()));
                    }
                    cameraEntities.add(cameraEntity);
                  }
                  existCodes.add(deviceCode);
                }
              });
            }
          });

        }
      });
    }
    return cameraEntities;
  }
}
